
#include "tp_typedef.h"
#include "tp_pipe.h"
#include "tp_memory.h"
#include "tp_log.h"

typedef struct _PrivInfo
{
    int read_fd;
    int write_fd;
}PrivInfo;

static int tp_pipe_win32socket_read(TpPipe* thiz, void* buffer, size_t len)
{
    int ret = 0;
    DECL_PRIV(thiz, priv);
    if(NULL != priv)
    {
        ret = recv(priv->read_fd, buffer, len, 0);
    }

    return ret > 0 ? ret : 0;
}

static int tp_pipe_win32socket_write(TpPipe* thiz, void* data, size_t size)
{
    int ret = 0;
    DECL_PRIV(thiz, priv);
    if(NULL != priv)
    {
        ret = send(priv->write_fd, data, size, 0);
    }

    return ret > 0 ? ret : 0;
}

static int tp_pipe_win32socket_get_read_fd(TpPipe* thiz)
{
    DECL_PRIV(thiz, priv);
    
    return NULL == priv ? priv->read_fd : -1;
}

static int tp_pipe_win32socket_get_write_fd(TpPipe* thiz)
{
    DECL_PRIV(thiz, priv);
 
    return NULL == priv ? priv->write_fd : -1;
}

static void tp_pipe_win32socket_destroy(TpPipe* thiz)
{
    DECL_PRIV(thiz, priv);
    if(NULL != thiz)
    {
        if(NULL != priv)
        {
            closesocket(priv->read_fd);
            closesocket(priv->write_fd);
        }

        tp_free(thiz);
    }

    return;
}

static TpRet make_socket_pair(int fds[2])
{
    int server_fd = -1;
    int client_fd = -1;
    int connect_fd = -1;
    struct sockaddr_in addr;
    int addr_len = 0;

    TpRet ret = TP_RET_OK;

    do
    {
        server_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(server_fd < 0)
        {
            ret = TP_RET_FAIL;
            break;
        }

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        addr.sin_port = htons(9980);

        if(bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
        {
            tp_loge("%s bind() failed, error:%d.", __FUNCTION__, WSAGetLastError());

            closesocket(server_fd);

            ret = TP_RET_FAIL;
            break;
        }

        listen(server_fd, 1);

        connect_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(connect_fd < 0)
        {
            closesocket(server_fd);

            ret = TP_RET_FAIL;
            break;
        }

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        addr.sin_port = htons(9980);
        if(connect(connect_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
        {
            closesocket(server_fd);
            closesocket(connect_fd);

            ret = TP_RET_FAIL;
            break;
        }

        addr_len = sizeof(addr);
        client_fd = accept(server_fd, (struct sockaddr*)&addr, &addr_len);
        if(client_fd < 0)
        {
            closesocket(server_fd);
            closesocket(connect_fd);
            closesocket(client_fd);

            ret = TP_RET_FAIL;
            break;
        }
        closesocket(server_fd);

        fds[0] = connect_fd;
        fds[1] = client_fd;
    }while(0);

    return ret;
}

TpPipe* tp_pipe_create(void)
{
    TpPipe* thiz = (TpPipe*)tp_alloc(sizeof(TpPipe) + sizeof(PrivInfo));
    if(NULL != thiz)
    {
        DECL_PRIV(thiz, priv);
        int fds[2] = {-1};

        if(TP_RET_OK == make_socket_pair(fds))
        {
            priv->read_fd = fds[0];
            priv->write_fd = fds[1];

            thiz->read = tp_pipe_win32socket_read;
            thiz->write = tp_pipe_win32socket_write;
            thiz->get_write_fd = tp_pipe_win32socket_get_write_fd;
            thiz->get_read_fd = tp_pipe_win32socket_get_read_fd;
            thiz->destroy = tp_pipe_win32socket_destroy;
        }
        else
        {
            tp_loge("%s: make_socket_pair() failed.", __FUNCTION__);

            tp_free(thiz);
            thiz = NULL;
        }
    }

    return thiz;
}